package com.chinamsp.dydb.mybatis.interceptor;

import java.util.ArrayList;
import java.util.Map;
import java.util.Properties;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.util.StringUtils;

import com.chinamsp.dydb.query.Pagination;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInterceptor;

/**
 * 分页拦截器，底层使用
 * 
 * @author shifenghu
 *
 */
@Intercepts({
		@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
				RowBounds.class, ResultHandler.class }),
		@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
				RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class }) })
public class PaginationInterceptor implements Interceptor {

	private static PageInterceptor pageInterceptor;

	public PaginationInterceptor() {
		pageInterceptor = new PageInterceptor();
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		// 从方法参数中获取 pagination
		MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
		Object parameter = invocation.getArgs()[1];
		BoundSql boundSql = mappedStatement.getBoundSql(parameter);
		if (boundSql == null || StringUtils.isEmpty(boundSql.getSql())) {
			return null;
		}
		Object parameterObject = boundSql.getParameterObject();
		Pagination pagination = null;
		if (parameterObject != null) {
			pagination = (Pagination) getParameterByType(parameterObject, Pagination.class);
		}
		// 如果 pagination 不存在或者已经初始化过分页信息（total count > 0）(pageSize <= 0)，则直接忽略
		if (pagination == null || pagination.getTotalCount() > 0 || pagination.getPageSize() <= 0) {
			return invocation.proceed();
		}
		Page<?> page = null;
		try {
			// 设置 pageheler 的 起始页
			PageHelper.startPage(pagination.getCurrentPage(), pagination.getPageSize());
			page = PageHelper.getLocalPage();
			Object obj = pageInterceptor.intercept(invocation);
			// 如果是分页结果则转换成 原始的 arraylist
			if (obj != null && Page.class.isAssignableFrom(obj.getClass())) {
				return new ArrayList(((Page<?>) obj).getResult());
			}
			return obj;
		} finally {
			if (page != null) {
				// 初始化分页参数
				pagination.init((int) page.getTotal(), page.getPageSize(), page.getPageNum());
			}
			PageHelper.clearPage();
		}
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {
		pageInterceptor.setProperties(properties);
	}

	/**
	 * 取参数中的类型
	 * 
	 * @param parameterObject
	 * @param parameterType
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private <T> T getParameterByType(Object parameterObject, Class<T> parameterType) {
		if (parameterObject instanceof Map) {
			Map map = (Map) parameterObject;
			for (Object parameter : map.values()) {
				if (parameter == null) {
					continue;
				}
				if (parameterType.isAssignableFrom(parameter.getClass())) {
					return (T) parameter;
				}
			}
		} else if (parameterType.isAssignableFrom(parameterObject.getClass())) {
			return (T) parameterObject;
		}
		return null;
	}
}